/************************************************************************
 * @file AbstractCommand.cpp
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QJsonValue>

#include <plugin/command/AbstractCommand.h>

namespace antu
{
namespace plugin
{
AbstractCommand::AbstractCommand()
{
}

QString AbstractCommand::usage()
{
    list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersIter = this->m_oRequiredInputParameters.begin();
    list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersEnd = this->m_oRequiredInputParameters.end();

    QVariantMap mappedTypes;
    for (; requiredParametersIter != requiredParametersEnd; requiredParametersIter++)
    {
        mappedTypes.insert(requiredParametersIter->first, QMetaType::typeName(requiredParametersIter->second));
    }
    QJsonDocument doc(QJsonObject::fromVariantMap(mappedTypes));
    QByteArray content = doc.toJson();
    return QString(content);
}

AbstractCommand::CommandReturnType AbstractCommand::execute(CommandParameters &context, const CommandParameters &inputs, CommandParameters &outputs)
{
    if (!this->isInputValid(inputs))
        return AbstractCommand::ParametersValidationFailure;
    return this->executeCommand(context, inputs, outputs);
}

bool AbstractCommand::parseInputs(const QString &inputStrings, CommandParameters &inputs)
{
    QJsonParseError error;
    QJsonDocument doc = QJsonDocument::fromJson(inputStrings.toLocal8Bit(), &error);

    if (error.error == QJsonParseError::NoError)
    {
        if (doc.isNull() || doc.isEmpty())
        {
            if (this->m_oRequiredInputParameters.size() == 0)
                return true;
            else
                return false;
        }
        else if (doc.isObject())
        {
            // check JSON object
            QJsonObject obj = doc.object();

            if (obj.size() != this->m_oRequiredInputParameters.size())
                return false;

            list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersIter = this->m_oRequiredInputParameters.begin();
            list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersEnd = this->m_oRequiredInputParameters.end();

            QVariantMap jsonMap = obj.toVariantMap();
            for (int i = 0; requiredParametersIter != requiredParametersEnd; requiredParametersIter++, i++)
            {
                QString paramName = requiredParametersIter->first;

                if (jsonMap.find(paramName) == jsonMap.end())
                {
                    inputs.clear();
                    return false;
                }

                QVariant value = jsonMap[paramName];
                if (value.type() != requiredParametersIter->second)
                {
                    inputs.clear();
                    return false;
                }

                inputs.insert(pair<QString, QVariant>(paramName, value));
            }
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

QString AbstractCommand::toJson(const CommandParameters &params)
{
    QVariantMap mappedParams;

    ConstCommandParameterIterator iter = params.begin();
    ConstCommandParameterIterator end = params.end();

    for (; iter != end; iter++)
    {
        mappedParams.insert(iter->first, iter->second);
    }

    QJsonDocument doc(QJsonObject::fromVariantMap(mappedParams));
    QByteArray content = doc.toJson();
    return QString(content);
}

bool AbstractCommand::isInputValid(const CommandParameters& inputs) const
{
    ConstCommandParameterIterator end = inputs.end();
    list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersIter = this->m_oRequiredInputParameters.begin();
    list<pair<QString, QMetaType::Type> >::const_iterator requiredParametersEnd = this->m_oRequiredInputParameters.end();

    for (; requiredParametersIter != requiredParametersEnd; requiredParametersIter++)
    {
        ConstCommandParameterIterator iter = inputs.find(requiredParametersIter->first);
        if (iter == end ||
                requiredParametersIter->second != iter->second.type())
        {
            return false;
        }
    }
    return true;
}

}
}
